﻿using System.Xml.Linq;
using System;
using System.Xml;
using System.Linq;
using System.Collections.Generic;
#if FALSE
    using System.Xml.XPath;
using System.Collections;
#endif

namespace MichMan.Utilities
{
    public class XElementNamespaceResolver : IXmlNamespaceResolver
    {
        XElement root;
        public XElementNamespaceResolver(XElement element)
        {
            if (element != null && element.Document != null)
            {
                root = element.Document.Root;
            }
            else
            {
                root = element;
            }
        }

        public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope)
        {
            return new Dictionary<string,string>();
        }

        public string LookupNamespace(string prefix)
        {
            return root.GetNamespaceOfPrefix(prefix).NamespaceName;
        }

        public string LookupPrefix(string namespaceName)
        {
            return String.Empty;
        }
    }

    public class XElementNameTable : NameTable
    {
        XElement root;
        public XElementNameTable(XElement element)
        {
            if (element != null && element.Document != null)
            {
                root = element.Document.Root;
            }
        }

        public override string Add(char[] key, int start, int len)
        {
            return base.Add(key, start, len);
        }

        public override string Add(string key)
        {
            return base.Add(key);
        }
        public override string Get(char[] key, int start, int len)
        {
            return base.Get(key, start, len);
        }

        public override string Get(string value)
        {
            return base.Get(value);
        }
    }

    public static partial class Extensions
    {

        public static void Set(this XElement elt, string id, object value)
        {
            if (value == null || elt == null || String.IsNullOrEmpty(id))
            {
                return;
            }

            XElement idElt = elt.GetElementById(id);
            if (idElt != null)
            {
                idElt.Value = value.ToString();
            }
        }

        public static XElement GetElementById(this XElement elt, string id)
        {
            if (elt == null || String.IsNullOrEmpty(id))
            {
                return null;
            }
            return elt.Descendants().FirstOrDefault(e => id == e.GetAttributeValue("id"));
        }

        public static string GetAttributeValue(this XElement elt, string attr)
        {
            if (elt == null || String.IsNullOrEmpty(attr))
            {
                return null;
            }

            XAttribute attribute = elt.Attribute(XName.Get(attr));
            if (attribute != null)
            {
                return attribute.Value;
            }
            return null;
        }
    }

    /// <summary>
    /// XPath access for XElement.  
    /// </summary>
    public static partial class Extensions
    {
#if FALSE
        /// <summary>
        /// True XPath binding support
        /// </summary>
        /// <param name="root"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetXPathValue(this XElement root, string path)
        {
            object o = root.XPathEvaluate(path, new XElementNamespaceResolver(root) );
            if (o != null)
            {
                XObject xobj = ((IEnumerable)o).Cast<XObject>().FirstOrDefault();
                if (xobj != null)
                {
                    if (xobj is XElement)
                    {
                        return ((XElement)xobj).Value;
                    }
                    if (xobj is XAttribute)
                    {
                        return ((XAttribute)xobj).Value;
                    }
                }
            }
            return null;
        }
#endif

        public static string GetZPathValue(this XElement root, string path)
        {
            XObject xobj = root.GetZPath<XObject>(path);
            if (xobj == null)
            {
                return String.Empty;
            }

            if (xobj is XElement)
            {
                return ((XElement)xobj).Value;
            }
            if (xobj is XAttribute)
            {
                return ((XAttribute)xobj).Value;
            }
            return String.Empty;
        }

        /// <summary>
        /// Returns the single object associated with an ZPath.  
        /// 
        /// ZPath is a simplified XPath - no namespaces.  
        /// Supports 
        /// /path/path 
        /// /path/path/@attribute
        /// /path/path[@attribute]
        /// /path/collection[0]/path/@attribute
        /// 
        /// </summary>
        public static T GetZPath<T>(this XElement root, string zpath) where T : XObject
        {
            if (root == null)
            {
                return null;
            }

            string attribute = null;

            zpath = zpath.Replace("[", "/");
            zpath = zpath.Replace("]", "/");

            string[] pathparts = zpath.Split('@');

            if (pathparts.Length == 2)
            {
                attribute = pathparts[1];
            }

            pathparts = pathparts[0].Split('/');

            XElement cur = root;
            foreach (var pathpart in pathparts)
            {
                string part = pathpart;
                if (cur != null)
                {
                    if (String.IsNullOrEmpty(part))
                    {
                        continue;
                    }

                    if (part == ".")
                    {
                        continue;
                    }

                    var nextcur = cur.Descendants().FirstOrDefault(d => d.Name.LocalName == part);
                    if (nextcur == null)
                    {
                        // maybe it was an index.
                        int idx;
                        if (int.TryParse(part, out idx))
                        {
                            nextcur = cur.Parent.Descendants(cur.Name).ElementAt(idx);
                        }
                    }
                    cur = nextcur;
                }
            }

            if (cur != null)
            {
                if (!String.IsNullOrEmpty(attribute))
                {
                    if (cur.Attribute(attribute) != null)
                    {
                        return cur.Attribute(attribute) as T;
                    }
                    return null;
                }

                return cur as T;
            }

            return null;
        }
    }
}